﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Narwhunderful.Aphelia.Framework.Graphics
{

    public class DrawableComponent : UpdatableComponent, IDrawableComponent
    {

        IGraphicsDeviceService _deviceService;

        public DrawableComponent(Game game)
            : base(game)
        {
            _deviceService = game.Services.GetService(typeof(IGraphicsDeviceService)) as IGraphicsDeviceService;
            if (_deviceService == null) return; //TODO: Throw an exception instead of returning.
            _deviceService.DeviceCreated += new EventHandler<EventArgs>(InternalDeviceCreated);
            _deviceService.DeviceResetting += new EventHandler<EventArgs>(InternalDeviceResetting);
            _deviceService.DeviceReset += new EventHandler<EventArgs>(InternalDeviceReset);
            _deviceService.DeviceDisposing += new EventHandler<EventArgs>(InternalDeviceDisposing);
            if (_deviceService.GraphicsDevice != null) LoadContent();
        }

        #region IDrawableComponent

        public event EventHandler<EventArgs> DrawOrderChanged = null;
        public event EventHandler<EventArgs> IsVisibleChanged = null;

        int _drawOrder = 0;
        public virtual int DrawOrder
        {
            get
            {
                return _drawOrder;
            }
            set
            {
                if (_drawOrder != value)
                {
                    _drawOrder = value;
                    OnDrawOrderChanged();
                }
            }
        }

        bool _isVisible = true;
        public virtual bool IsVisible
        {
            get
            {
                return _isVisible;
            }
            set
            {
                if (_isVisible != value)
                {
                    _isVisible = value;
                    OnIsVisibleChanged();
                }
            }
        }

        bool _isDrawing = false;
        public bool IsDrawing
        {
            get
            {
                return _isDrawing;
            }
            protected internal set
            {
                _isDrawing = value;
            }
        }

        bool _backgroundIsVisible = false;
        public bool BackgroundIsVisible
        {
            get
            {
                return _backgroundIsVisible;
            }
            set
            {
                _backgroundIsVisible = value;
            }
        }

        bool _middlegroundIsVisible = false;
        public bool MiddlegroundIsVisible
        {
            get
            {
                return _middlegroundIsVisible;
            }
            set
            {
                _middlegroundIsVisible = value;
            }
        }

        bool _foregroundIsVisible = false;
        public bool ForegroundIsVisible
        {
            get
            {
                return _foregroundIsVisible;
            }
            set
            {
                _foregroundIsVisible = value;
            }
        }

        public virtual void Draw(GameTime gameTime)
        {
            if (CanDraw(gameTime))
            {
                BeginDraw();
                if (CanDrawBackground(gameTime)) InternalDrawBackground(gameTime);
                if (CanDrawMiddleground(gameTime)) InternalDrawMiddleground(gameTime);
                if (CanDrawForeground(gameTime)) InternalDrawForeground(gameTime);
                EndDraw();
            }
        }

        public virtual bool CanDraw(GameTime gameTime)
        {
            if (_isDrawing) return false;
            if (!_isVisible) return false;
            return !base.IsDesigning;
        }

        protected virtual void BeginDraw()
        {
            _isDrawing = true;
        }

        protected virtual bool CanDrawBackground(GameTime gameTime)
        {
            return _backgroundIsVisible;
        }

        protected virtual bool CanDrawMiddleground(GameTime gameTime)
        {
            return _middlegroundIsVisible;
        }

        protected virtual bool CanDrawForeground(GameTime gameTime)
        {
            return _foregroundIsVisible;
        }

        protected virtual void InternalDrawBackground(GameTime gameTime)
        {
        }

        protected virtual void InternalDrawMiddleground(GameTime gameTime)
        {
        }

        protected virtual void InternalDrawForeground(GameTime gameTime)
        {
        }

        protected virtual void EndDraw()
        {
            _isDrawing = false;
        }

        protected virtual void OnDrawOrderChanged()
        {
            DrawOrderChanged.SafeInvoke(this, new EventArgs());
        }

        protected virtual void OnIsVisibleChanged()
        {
            IsVisibleChanged.SafeInvoke(this, new EventArgs());
        }

        #endregion

        #region IDisposableComponent

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                UnloadContent();
                if (_deviceService != null)
                {
                    _deviceService.DeviceCreated -= new EventHandler<System.EventArgs>(InternalDeviceCreated);
                    _deviceService.DeviceResetting -= new EventHandler<System.EventArgs>(InternalDeviceResetting);
                    _deviceService.DeviceReset -= new EventHandler<System.EventArgs>(InternalDeviceReset);
                    _deviceService.DeviceDisposing -= new EventHandler<System.EventArgs>(InternalDeviceDisposing);
                }
            }
            base.Dispose(disposing);
        }

        #endregion

        protected virtual void LoadContent()
        {
        }

        protected virtual void UnloadContent()
        {
        }

        protected virtual void DeviceCreated()
        {
            LoadContent();
        }

        protected virtual void DeviceDisposing()
        {
            UnloadContent();
        }

        protected virtual void DeviceReset()
        {
        }

        protected virtual void DeviceResetting()
        {
        }

        internal void InternalDeviceCreated(object sender, EventArgs e)
        {
            DeviceCreated();
        }

        internal void InternalDeviceDisposing(object sender, EventArgs e)
        {
            DeviceDisposing();
        }

        internal void InternalDeviceReset(object sender, EventArgs e)
        {
            DeviceDisposing();
        }

        internal void InternalDeviceResetting(object sender, EventArgs e)
        {
            DeviceResetting();
        }

    }

}
